昨天我們鋪設了一片 100x100 的廣大平原,作為未來地圖生成的基礎。然而,這塊畫布目前面臨一個物理限制:它是一塊絕對平坦的單一幾何體,無法呈現出《歧路旅人》那種泥土小徑、草地邊緣與高低落差交織的豐富層次。
為了解決這個問題,今天我們要將昨天的巨大平原「網格化」,引入 2D 遊戲最核心的圖塊 (Tiles) 概念。我們將利用二維陣列作為設計圖,可讓程式碼自動拼裝出高低起伏的地形。
首先,請打開 src/main.js,將昨天 floor(大地板)相關的程式碼全部刪除。我們不再依賴單一平面,而是改用一個二維陣列來定義地形。
在這個陣列中,數字代表了不同的地貌:
0:草地
1:泥土小路
2:稍微隆起的石頭或障礙物
可自行定義需要的圖案
// 1. 定義地圖陣列 (這裡先用 12x12 作為示範,未來可以無限擴張)
const mapGrid = [
[0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 2, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 2, 1, 1, 0, 0, 1, 1, 0, 0, 0],
[0, 2, 2, 1, 0, 0, 0, 0, 1, 1, 0, 0],
[0, 2, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 2, 2, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
【Step 2:載入專屬像素貼圖】
請將準備好的三張圖片(grass.png, dirt.png, stone.png)放進專案的 public 資料夾中。
接著,我們透過 TextureLoader 把它們載入,並務必開啟 NearestFilter,這能確保你的像素藝術在 3D 空間中依然保持銳利、完美的顆粒感,而不會被引擎模糊化。
// 2. 準備建材的幾何體與材質
const tileSize = 1;
const tileGeo = new THREE.BoxGeometry(tileSize, 1, tileSize);
const textureLoader = new THREE.TextureLoader();
// 載入圖片並保持像素銳利化
const grassTex = textureLoader.load('/grass.png');
grassTex.magFilter = THREE.NearestFilter;
const dirtTex = textureLoader.load('/dirt.png');
dirtTex.magFilter = THREE.NearestFilter;
const stoneTex = textureLoader.load('/stone.png');
stoneTex.magFilter = THREE.NearestFilter;
// 建立對應的標準材質
const materials = {
0: new THREE.MeshStandardMaterial({ map: grassTex }), // 草地
1: new THREE.MeshStandardMaterial({ map: dirtTex }), // 泥土
2: new THREE.MeshStandardMaterial({ map: stoneTex }) // 石塊
};
【Step 3:陣列掃描與實例化地形】
最後是今天的重頭戲。利用雙層 for 迴圈掃描 mapGrid,根據讀取到的數字生成對應的方塊。請特別注意程式碼中對 y 軸(高度)的微調,這會讓石頭隆起,讓地圖瞬間具備微縮模型的立體感!
// 3. 掃描陣列並生成 3D 實體
const mapSize = mapGrid.length;
for (let row = 0; row < mapSize; row++) {
for (let col = 0; col < mapGrid[row].length; col++) {
const tileType = mapGrid[row][col];
// 生成方塊並自動套用對應的像素貼圖
const tile = new THREE.Mesh(tileGeo, materials[tileType]);
// 計算 X 與 Z 座標 (讓整張地圖置中)
tile.position.x = col - (mapSize / 2) + 0.5;
tile.position.z = row - (mapSize / 2) + 0.5;
// --- 關鍵:高度微調 ---
if (tileType === 2) {
// 石頭障礙物稍微加高
tile.scale.y = 1.5;
tile.position.y = 1.5 / 2;
} else {
// 草地與泥土是平的
tile.position.y = 1 / 2;
}
scene.add(tile);
}
}
【本日結語】
今天的地圖主要還是以陣列的方式完成,雖然能夠快速建立出基本的地圖結構,但隨著地圖規模變大,手動輸入的方式也變得越來越繁瑣。
因此,明天預計會著重在解決「地圖太小」以及「手動建立地圖效率太低」的問題,嘗試改用演算法來自動生成更大、更有變化的地圖。
我們明天見。